<?php

namespace Event\Drive\Service;

use Event\Drive\Data\Cache\Cache;
use Event\Drive\Exceptions\EventDriveException;
use Illuminate\Support\Facades\DB;

abstract class RegisterEventManageService
{
    /**
     * @var string
     */
    private $cacheKey;

    /**
     * @var Cache
     */
    private $cacheService;

    public function __construct()
    {
        $this->cacheKey = config('eventdrive.cacheKey') ?: 'register_event_list';
        $this->cacheService = new Cache($this->cacheKey);
    }

    /**
     * 事件注册
     * @param string $eventClassName
     * @return void
     * @throws EventDriveException
     */
    public function register(string $eventClassName)
    {
        try {
            //判断事件是否存在
            if ($this->eventExists($eventClassName)) {
                throw new EventDriveException($eventClassName,'Event registered');
            }
            $this->createDBRegisterEvent(['event_class_name' => $eventClassName]);
        } catch (\Exception $exception) {
            throw new EventDriveException($eventClassName,'Event registration DB operation failed');
        }
    }

    /**
     * 事件注销
     * @param string $eventClassName
     * @return void
     * @throws EventDriveException
     * @throws \Throwable
     */
    public function unregister(string $eventClassName)
    {
        //判断事件是否存在
        if (!$this->eventExists($eventClassName)) {
            throw new EventDriveException(
                $eventClassName,
                'The event has not been registered or has been cancelled'
            );
        }
        //防止出现数据不一致的问题
        try {
            $this->cacheService->delEvent($eventClassName);
        } catch (\Exception $exception) {
            throw new EventDriveException($eventClassName,'Busy operation');
        }
        DB::beginTransaction();
        try {
            $this->deleteDBRegisterEvent($eventClassName);
            DB::commit();

        } catch (\Exception $exception) {
            throw new EventDriveException($eventClassName,'Event deregistration DB operation failed');
        }
    }

    /**
     * 事件是否存在
     * @param string $eventClassName
     * @return bool
     * @throws EventDriveException
     */
    public function eventExists(string $eventClassName) : bool
    {
        try {
            //一定要防止缓存穿透-后期高并发需要优化
            if ($this->cacheService->getEvent($eventClassName)) {
                return true;
            }
            $registerEvent = $this->getDBRegisterEventByEventClassName($eventClassName);
            if (!$registerEvent) {
                return false;
            }
            $this->cacheService->setEvent($registerEvent['event_class_name'], 1);
            return true;
        } catch (\Exception $exception) {
            throw new EventDriveException($eventClassName,'Check event exists DB operation failed');
        }
    }


    /**
     * 获取事件列表
     * @return array
     * @throws EventDriveException
     */
    public function getEventList() : array
    {
        try {
            return $this->getDBRegisterEventList();
        } catch (\Exception $exception) {
            throw new EventDriveException('All events','Get event list DB operation failed');
        }
    }

    /**
     * 创建DB事件注册
     * @param array $data
     * @return void
     */
    protected abstract function createDBRegisterEvent(array $data) : void;

    /**
     * 获取DB事件注册List
     * @return array
     */
    protected abstract function getDBRegisterEventList() : array;

    /**
     * 通过事件名称判断事件是否存在
     * @param string $eventClassName
     * @return array
     */
    protected abstract function getDBRegisterEventByEventClassName(string $eventClassName) : array;

    /**
     * 通过事件名称删除DB事件注册
     * @param string $eventClassName
     * @return void
     */
    protected abstract function deleteDBRegisterEvent(string $eventClassName) : void;
}
